﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Diagnostics;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace DarwinIsDead {
    /// <summary>
    /// Class that represents a cohesive Zombie entity.  A Zombie is
    /// essentially a collection of Traits.
    /// </summary>
    public class Zombie : Entity{
        private int health;

        private int birthTime;
        private int deathTime;

        private int hurt;
        private int hurtCount;

        public delegate void TimedEvent(int time);
        public TimedEvent KillEvent { get; set; }

        /// <summary>
        /// Returns or sets this <see cref="Zombie"/>'s maximum health.
        /// A Zombie's health cannot exceed its maximum health.
        /// </summary>
        public int MaxHealth { get; set; }

        /// <summary>
        /// Returns whether or not this <see cref="Zombie"/> is Alive.
        /// </summary>
        public bool Alive { get; set; }

        /// <summary>
        /// Returns or sets this <see cref="Zombie"/>'s health.
        /// </summary>
        public int Health {
            get { return health; }
            set { health = (value <= MaxHealth) ? value : MaxHealth; }
        }

        /// <summary>
        /// FFFFUUUUHHH
        /// </summary>
        public List<Trait> Traits { get; private set; }

        /// <summary>
        /// Retrieves the total damage this <see cref="Zombie"/> has done to
        /// the player.
        /// </summary>
        public int TotalDamage { get; set; }

        public float Distance { get; set; }

        public Color Color;

        /// <summary>
        /// Returns the time between a <see cref="Zombie"/>'s birth and 
        /// death, in seconds.
        /// </summary>
        public int Lifetime {
            get { return (deathTime - birthTime); }
        }

        /// <summary>
        /// Create a new Zombie with the specified hitbox width and height.
        /// By default, the Zombie is positioned at (0, 0).  Calling
        /// Zombie.Spawn() 
        /// </summary>
        /// <param name="world">Reference to the world</param>
        /// <param name="w">Width of the Zombie's hitbox</param>
        /// <param name="h">Height of the Zombie's hitbox</param>
        public Zombie(World world)
            : base(world, 0f, 0f, 0f, 0f, true) {
            
            Alive = false;
            health = 0;
            birthTime = 0;
            deathTime = 0;
            TotalDamage = 0;

            Traits = new List<Trait>();

            hurt = 0;
            hurtCount = 0;
            Color = new Color(Vector3.One);
            /*debugRect = new Texture2D(World.Game.GraphicsDevice, Round(w), Round(h));
            Color[] data = new Color[Round(w*h)];
            for (int i = 0; i < data.Length; ++i) data[i] = Color.Chocolate;
            debugRect.SetData(data);*/
        }

        /// <summary>
        /// Releases the resources used by each of this <see cref="Zombie"/>'s
        /// <see cref="Trait"/>s.
        /// <seealso cref="Trait.Dispose()"/>
        /// </summary>
        public override void Dispose() {
            foreach (Trait trait in Traits) {
                trait.Dispose();
            }
        }

        /// <summary>
        /// Births a <see cref="Zombie"/>
        /// </summary>
        /// <param name="x">x-coordinate</param>
        /// <param name="y">y-coordinate</param>
        /// <param name="time">game time, in seconds, of the zombie's birth
        /// </param>
        public void Spawn(float x, float y, int time) {
            Position = new Vector2(x, y);
            birthTime = time;
            health = MaxHealth;
            Alive = true;
            Solid = true;
        }

        /// <summary>
        /// Disables a <see cref="Zombie"/>
        /// </summary>
        /// <param name="time">Time of death, in seconds</param>
        public void Kill(int time) {
            deathTime = time;
            Alive = false;
            Solid = false;

            // Alert ZombieFactory
            if (KillEvent != null) {
                KillEvent(time);
            }
        }

        /// <summary>
        /// Adds to the total damage this <see cref="Zombie"/> does in a
        /// lifetime.
        /// </summary>
        /// <param name="damage">damage count to add</param>
        public void AddDamage(int damage) {
            TotalDamage += damage;
        }

        /// <summary>
        /// Adds a <see cref="Trait"/> to the Zombie's DNA.
        /// </summary>
        /// <param name="trait">Trait to add</param>
        public void AddTrait(Trait trait) {
            Traits.Add(trait);
        }

        /// <summary>
        /// Returns a list of Traits with a specified Type.
        /// </summary>
        /// <typeparam name="T">Type of traits to return</typeparam>
        /// <returns></returns>
        public List<Trait> GetTraits<T>() {
            List<Trait> ret = new List<Trait>();
            foreach (Trait trait in Traits) {
                ret.Add(trait);
            }
            return ret;
        }

        /// <summary>
        /// Handles collisions with other entities.
        /// </summary>
        /// <remarks>
        /// Propogates collision data to each of this <see cref="Zombie"/>'s traits
        /// </remarks>
        /// <param name="other"></param>
        /// <param name="overlap"></param>
        public override void Collision(Entity other, Rectangle overlap) {
            // Take damage from bullets
            if (other is Bullet) {
                Bullet bullet = (Bullet)other;
                if (bullet.Friendly) {
                    Health -= bullet.Damage;
                    hurt = bullet.Damage;
                    hurtCount = Math.Abs(hurt);
                }
            }

            foreach (Trait trait in Traits) {
                trait.Collision(other, overlap);
            }
        }

        /// <summary>
        /// Update
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Update(GameTime gameTime) {
            if (Alive) {
                if (health <= 0) {
                    Kill(gameTime.TotalGameTime.Seconds);
                } else {
                    if (hurt > 0) {
                        Color.G = (byte)(255 - hurt * (255 / hurtCount));
                        Color.B = (byte)(255 - hurt * (255 / hurtCount));
                        hurt--;
                    } else if (hurt < 0) {
                        int h = Math.Abs(hurt);
                        Color.R = (byte)(255 - h * (255 / hurtCount));
                        Color.B = (byte)(255 - h * (255 / hurtCount));
                        hurt++;
                    } else {
                        Color.R = 255;
                        Color.G = 255;
                        Color.B = 255;
                    }

                    foreach (Trait trait in Traits) {
                        trait.Update(gameTime);
                    }

                    World.CheckCollisions(this);
                }
                Depth = HitBox.Y + (HitBox.Width / 2);
            }
        }

        /// <summary>
        /// Draw
        /// </summary>
        /// <param name="spriteBatch"></param>
        public override void Draw(SpriteBatch spriteBatch) {
            if (Alive) {
                foreach (Trait trait in Traits) {
                    trait.Draw(spriteBatch, Color);
                }
                //Debug.WriteLine("Damage: " + TotalDamage);
            }
        }

        public override void Move(Vector2 dp) {
            base.Move(dp);
            Distance += (float)Math.Sqrt(dp.X * dp.X + dp.Y * dp.Y);
        }

        public override void Move(float dx, float dy) {
            base.Move(dx, dy);
            Distance += (float)Math.Sqrt(dx * dx + dy * dy);
        }
    }
}
